home *** CD-ROM | disk | FTP | other *** search
/ The Arsenal Files 8 / The Arsenal Files Collection #8 (Arsenal Computer) (1996).ISO / prg_casm / cppnl017.zip / CPPNL017.TXT < prev    next >
Text File  |  1996-12-09  |  14KB  |  464 lines

  1. Issue #017
  2. November, 1996
  3.  
  4.  
  5. Contents:
  6.  
  7. Introduction to Exception Handling Part 3 - Stack Unwinding
  8. Notes From ANSI/ISO - Template Compilation Model Part 2
  9. Using C++ as a Better C Part 17 - Empty Classes
  10. Transitioning to C++
  11. Introduction to STL Part 4 - Maps
  12.  
  13.  
  14. INTRODUCTION TO EXCEPTION HANDLING PART 3 - STACK UNWINDING
  15.  
  16. In the last issue we talked about throwing exceptions.  Before
  17. discussing how exceptions are handled, we need to talk about an
  18. intermediate step, stack unwinding.
  19.  
  20. The exception handling mechanism is dynamic in that a record is kept
  21. of the flow of program execution, for example via stack frames and
  22. program counter mapping tables.  When an exception is thrown, control
  23. transfers to the nearest suitable handler.  "nearest" in this sense
  24. means the nearest dynamically surrounding try block containing a
  25. handler that matches the type of the thrown exception.  We will talk
  26. more about exception handlers in a future issue.
  27.  
  28. Transfer of control from the point at which an exception is thrown to
  29. the exception handler implies jumping out of one program context into
  30. another.  What about cleanup of the old program context?  For example,
  31. what about local class objects that have been allocated?  Are their
  32. destructors called?
  33.  
  34. The answer is "yes".  All stack-allocated ("automatic") objects
  35. allocated since the try block was entered will have their destructors
  36. invoked.  Let's look at an example:
  37.  
  38.         #include <iostream.h>
  39.  
  40.         class A {
  41.                 int x;
  42.         public:
  43.                 A(int i) {x = i; cerr << "ctor " << x << endl;}
  44.                 ~A() {cerr << "dtor " << x << endl;}
  45.         };
  46.  
  47.         void f()
  48.         {
  49.                 A a1(1);
  50.  
  51.                 throw "this is a test";
  52.  
  53.                 A a2(2);
  54.         }
  55.  
  56.         int main()
  57.         {
  58.                 try {
  59.                         A a3(3);
  60.  
  61.                         f();
  62.  
  63.                         A a4(4);
  64.                 }
  65.                 catch (const char* s) {
  66.                         cerr << "exception: " << s << endl;
  67.                 }
  68.  
  69.                 return 0;
  70.         }
  71.  
  72. Output of this program is:
  73.  
  74.         ctor 3
  75.         ctor 1
  76.         dtor 1
  77.         dtor 3
  78.         exception: this is a test
  79.  
  80. In this example, we enter the try block in main(), allocate a3, then
  81. call f().  f() allocates a1, then throws an exception, which will
  82. transfer control to the catch clause in main().
  83.  
  84. In this example, the a1 and a3 objects have their destructors called.
  85. a2 and a4 do not, because they were never allocated.
  86.  
  87. It's possible to have class objects containing other class objects, or
  88. arrays of class objects, with partial construction taking place
  89. followed by an exception being thrown.  In this case, only the
  90. constructed subobjects will be destructed.
  91.  
  92.  
  93. NOTES FROM ANSI/ISO - TEMPLATE COMPILATION MODEL PART 2
  94. Jonathan Schilling, jls@sco.com
  95.  
  96. In the last issue, we looked at the "inclusion" model of template
  97. compilation, which is the one used by most compilers in practice but
  98. which is lacking in several respects.  As a reminder, here was the
  99. example that illustrated name leakage in the inclusion model:
  100.  
  101. file3.h:
  102.         template <class T> void f(T);
  103.         #include "file3.C"
  104.  
  105. file3.C:
  106.         void g(int);
  107.         template <class T> void f(T t) {
  108.                 g(0);
  109.         }
  110.  
  111. caller3.C:
  112.         #include "file3.h"
  113.         void g(long);
  114.         void h() {
  115.                 f(3.14);
  116.                 g(1);   // should call g(long), but calls g(int) instead
  117.         }
  118.  
  119. Now we'll look at the newly-specified template separate compilation
  120. model that has recently been added to the standard.  There isn't space
  121. here to go into a full description of the new rules, and in fact the
  122. complexity of this subject rapidly approaches infinity!  But here are
  123. some of the key highlights:
  124.  
  125.         Names in template functions are divided into those that are 
  126.         dependent upon the template arguments, and those that are not.  
  127.         This distinction is made syntactically, making it easier for 
  128.         people and compilers to understand.
  129.  
  130.         Names in template functions that are not dependent upon the 
  131.         template arguments are resolved only in the template definition
  132.         context (an example would be g(0) in file3.C above).
  133.  
  134.         Names in template functions that are dependent upon the template
  135.         arguments are resolved either in the template instantiation 
  136.         context (using external names that may be found in object code 
  137.         symbol tables) or in the template definition context.  In the 
  138.         case of nested or transitive instantiations, no "intermediate 
  139.         context" is available.
  140.  
  141.         Instantiation of template functions is made "position 
  142.         independent", meaning that if the meaning of a program changes 
  143.         depending upon where instantiations are placed, program behavior
  144.         is undefined.
  145.  
  146.         Instantiations may be performed at either compile- or link-time.  
  147.         If the choice makes a difference, program behavior is undefined.
  148.         An implementation is allowed to place compilation-order 
  149.         restrictions on separately-compiled templates.
  150.  
  151.         Separate compilation of templates is not done by default:  the 
  152.         template declaration or definition must use the new keyword 
  153.         "export" in order for it to happen.  Otherwise the inclusion 
  154.         method is used.  This will provide upward compatibility of 
  155.         existing template code.
  156.  
  157. Some of these changes involve the template instantiation model (see C++
  158. Newsletter #010) more than the template source model, but are necessary
  159. to make separate compilation workable.  
  160.  
  161. Here's the example from above, made into a separately-compiled
  162. template:
  163.  
  164. file4.h:
  165.         template <class T> void f(T);
  166.  
  167. file4.C:
  168.         void g(int);
  169.         export template <class T> void f(T t) {
  170.                 g(0);
  171.         }
  172.  
  173. caller4.C:
  174.         #include "file4.h"
  175.         void g(long);
  176.         void h() {
  177.                 f(3.14);
  178.                 g(1);   // now, this calls g(long); g(int) not visible
  179.         }
  180.  
  181. In this model, file4.C is compiled explicitly, as well as caller4.C,
  182. and its source is not pulled into the header, explicitly or
  183. implicitly.  The source is otherwise identical to the inclusion model
  184. except for the addition of the "export" keyword.  (The meaning of this
  185. keyword is somewhat similar to the existing "extern" keyword, and some
  186. people wanted to reuse that keyword rather than introduce a new one.
  187. After some debate, the committee decided at its recently concluded
  188. November meeting not to overload "extern".  Also note that the keyword
  189. may be placed on either the template declaration or the template
  190. definition; this flexibility may help library vendors in shipping
  191. products that can be used with either template compilation model).
  192.  
  193. One area of uncertainty is how much the "no intermediate context"
  194. limitation will affect real code.  Here's an example where it matters:
  195.  
  196. ic1.h:
  197.         export template <class T>
  198.         void g(const T&);
  199.  
  200. ic1.C:
  201.         export template <class T>
  202.         void g(const T& t)
  203.         {
  204.                 length(t);      // how does length get found?
  205.         }
  206.  
  207. ic2.h:
  208.         export template <class T> void f(T);
  209.  
  210. ic2.C:
  211.         #include "ic1.h"
  212.  
  213.         template <class T>
  214.         class Container { ... };
  215.  
  216.         export template <class T>
  217.         int length (const Container<T>&) { ... }
  218.  
  219.         export template <class T> void f(T t)
  220.         {
  221.                 Container<T> s;
  222.                 g(s);
  223.         }
  224.  
  225. ic3.C:
  226.         #include "ic2.h"
  227.  
  228.         class A { ... };
  229.  
  230.         void m() {
  231.                 A a;
  232.                 f(a);           // this starts the instantiations
  233.         }
  234.  
  235. This is a case of transitive instantiation, where m() instantiates f(A)
  236. which instantiates g(Container<A>).  Within g(), length(t) is a
  237. dependent name lookup, so it can find length either in the definition
  238. context (ic1.C) or in the instantiation context (ic3.C).  But it's in
  239. neither.  It's in ic2.C, which is considered "intermediate context".
  240. Thus this example would not compile as is, and would have to be
  241. recoded to use the inclusion method (basically, drop the "export"'s
  242. and include the .C's into the .h's). 
  243.  
  244. It is an open question how common this kind of intermediate context
  245. problem will be.  One analysis found no cases of it in the
  246. template-intensive Standard Template Library, which may be encouraging.
  247. As with many of the new inventions of the C++ standardization process,
  248. only time will tell.
  249.  
  250.  
  251. USING C++ AS A BETTER C PART 17 - EMPTY CLASSES
  252.  
  253. Here's a simple one.  In C, an empty struct like:
  254.  
  255.         struct A {};
  256.  
  257. is invalid, whereas in C++ usage like:
  258.  
  259.         struct A {};
  260.  
  261. or:
  262.  
  263.         class B {};
  264.  
  265. is perfectly legal.  This type of construct is useful when developing
  266. a skeleton or placeholder for a class.
  267.  
  268. An empty class has size greater than zero.  Two class objects of empty
  269. classes will have distinct addresses, as in:
  270.  
  271.         class A {};
  272.  
  273.         void f()
  274.         {
  275.                 A* p1 = new A;
  276.                 A* p2 = new A;
  277.  
  278.                 // p1 != p2 at this point ...
  279.         }
  280.  
  281. There are still one or two C++ compilers that generate C code as their
  282. "assembly" language.  To handle an empty class, they will generate a
  283. dummy member, so for example:
  284.  
  285.         class A {};
  286.  
  287. becomes:
  288.  
  289.         struct A {
  290.                 char __dummy;
  291.         };
  292.  
  293. in the C output.
  294.  
  295.  
  296. TRANSITIONING TO C++
  297.  
  298. One of the services we offer is advice and support to organizations
  299. transitioning to C++ and object-oriented development.  Some of the
  300. aspects of the service include:
  301.  
  302.         - Advice on which compilers to use
  303.  
  304.         - Support via e-mail
  305.  
  306.         - Development of internal company newsletters
  307.  
  308.         - Training and development of tutorial information
  309.  
  310.         - Advice on which language features to use and avoid
  311.  
  312.         - Object-oriented design / designing for performance
  313.  
  314. If you'd like more information about how these services could be
  315. applied in your organization, please send mail to glenm@glenmccl.com.
  316.  
  317.  
  318. INTRODUCTION TO STL PART 4 - MAPS
  319.  
  320. In the previous issue we talked a bit about STL sets.  In this issue
  321. we'll discuss another data structure, maps.  A map is something like
  322. an associative array or hash table, in that each element consists of a
  323. key and an associated value.  A map must have unique keys, whereas
  324. with a multimap keys may be duplicated.
  325.  
  326. To see how maps work, let's look at a simple application that counts
  327. word frequency.  Words are input one per line and the total count of
  328. each is output.
  329.  
  330.         #include <iostream>
  331.         #include <string>
  332.         #include <map>
  333.  
  334.         using namespace std;
  335.  
  336.         int main()
  337.         {
  338.                 typedef map<string, long, less<string> > MAP;
  339.                 typedef MAP::value_type VAL;
  340.  
  341.                 MAP counter;
  342.  
  343.                 char buf[256];
  344.  
  345.                 while (cin >> buf)
  346.                         counter[buf]++;
  347.  
  348.                 MAP::iterator it = counter.begin();
  349.  
  350.                 while (it != counter.end()) {
  351.                         cout << (*it).first << " " << (*it).second << endl;
  352.                         it++;
  353.                 }
  354.  
  355.                 return 0;
  356.         }
  357.  
  358. This is a short but somewhat tricky example.  We first set up a
  359. typedef for:
  360.  
  361.         map<string, long, less<string> >
  362.  
  363. which is a map template with three template arguments.  The first is
  364. the type of the key, in this example a string.  The second is the
  365. value associated with the key, in this case a long integer used as a
  366. counter.  Finally, because the keys of the map are maintained in
  367. sorted order, we provide a template comparison function (see issue
  368. #016 for another example of this).
  369.  
  370. Another typedef we establish but do not use in this simple example is
  371. the VAL type, which is a template of type "pair<string,long>".  pair
  372. is used internally within STL, and in this case is used to represent a
  373. map element key/value pair.  So VAL represents an element in the map.
  374.  
  375. We then read lines of input and insert each word into the map.  The
  376. statement:
  377.  
  378.         counter[buf]++;
  379.  
  380. does several things.  First of all, buf is a char*, not a string, and
  381. must be converted via a constructor.  What we've said is equivalent to:
  382.  
  383.         counter[string(buf)]++;
  384.  
  385. operator[] is overloaded for maps, and in this case the key is used to
  386. look up the element, and return a long&, that is, a reference to the
  387. underlying value.  This value is then incremented (it started at zero).
  388.  
  389. Finally, we iterate over the map entries, using an iterator.  Note
  390. that:
  391.  
  392.         (*it).first
  393.  
  394. cannot be replaced by:
  395.  
  396.         it->first
  397.  
  398. because "*" is overloaded.  When * is applied to "it", it returns a
  399. pair<string,key> object, that is, the underlying type of elements in
  400. the map.  We then reference "first" and "second", fields in pair, to
  401. retrieve keys and values for output.
  402.  
  403. For input:
  404.  
  405.         a
  406.         b
  407.         c
  408.         a
  409.         b
  410.  
  411. output is:
  412.  
  413.         a 2
  414.         b 2
  415.         c 1
  416.  
  417. There are some complex ideas here, but map is a very powerful feature
  418. worth mastering.
  419.  
  420.  
  421. ACKNOWLEDGEMENTS
  422.  
  423. Thanks to Nathan Myers, Eric Nagler, David Nelson, Terry Rudd,
  424. Jonathan Schilling, Elaine Siegel, John Spicer, and Clay Wilson for
  425. help with proofreading.
  426.  
  427.  
  428. SUBSCRIPTION INFORMATION / BACK ISSUES
  429.  
  430. To subscribe to the newsletter, send mail to majordomo@world.std.com
  431. with this line as its message body:
  432.  
  433. subscribe c_plus_plus
  434.  
  435. Back issues are available via FTP from:
  436.  
  437.         rmi.net /pub2/glenm/newslett
  438.  
  439. or on the Web at:
  440.  
  441.         http://rainbow.rmi.net/~glenm
  442.  
  443. There is also a Java newsletter.  To subscribe to it, say:
  444.  
  445. subscribe java_letter
  446.  
  447. using the same majordomo@world.std.com address.
  448.  
  449. -------------------------
  450.  
  451. Copyright (c) 1996 Glen McCluskey.  All Rights Reserved.
  452.  
  453. This newsletter may be further distributed provided that it is copied
  454. in its entirety, including the newsletter number at the top and the
  455. copyright and contact information at the bottom.
  456.  
  457. Glen McCluskey & Associates
  458. Professional C++ Consulting
  459. Internet: glenm@glenmccl.com
  460. Phone: (800) 722-1613 or (970) 490-2462
  461. Fax: (970) 490-2463
  462. FTP: rmi.net /pub2/glenm/newslett (for back issues)
  463. Web: http://rainbow.rmi.net/~glenm
  464.